#set( $symbol_pound = '#' )
#set( $symbol_dollar = '$' )
#set( $symbol_escape = '\' )
/*
 * Copyright © 2014, Finium Sdn Bhd, All Rights Reserved
 * 
 * GenericDAOImpl.java
 * Modification History
 * *************************************************************
 * Date				Author			Comment
 * Aug 27, 2012			Venkaiah Chowdary Koneru			Created
 * *************************************************************
 */
package ${package}.commons.dao;

import static ${package}.AppConstants.SUPPRESS_WARNINGS_UNCHECKED;

import java.io.Serializable;
import java.util.List;

import org.springframework.util.StringUtils;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.GenericTypeResolver;

/**
 * Implementation of <code>GenericDAO</code> using Hibernate. The SessionFactory
 * property is annotated for automatic injection.
 * 
 * @param <T>
 *            The type of the entity object for which this instance is to be
 *            used.
 * @param <ID>
 *            The type of the id of the entity object for which this instance is
 *            to be used.
 * 
 * @author Venkaiah Chowdary Koneru
 */
public abstract class GenericDAOImpl<T, ID extends Serializable> implements
        GenericDAO<T, ID>
{
    @Autowired
    private SessionFactory sessionFactory;

    private final Class<T> entityClass;

    private final String entityClassName;

    /**
     * Get the specified entity type from the DAO
     */
    @SuppressWarnings(SUPPRESS_WARNINGS_UNCHECKED)
    public GenericDAOImpl()
    {
        Class<?>[] classes = GenericTypeResolver.resolveTypeArguments(
                getClass(), GenericDAOImpl.class);

        this.entityClass = (Class<T>) classes[0];
        this.entityClassName = this.entityClass.getName();
    }

    /**
     * {@inheritDoc}
     */
    @SuppressWarnings(SUPPRESS_WARNINGS_UNCHECKED)
    @Override
    public ID save(T newInstance)
    {
        return (ID) sessionFactory.getCurrentSession().save(newInstance);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void update(T transientObject)
    {
        sessionFactory.getCurrentSession().update(transientObject);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void delete(T persistentObject)
    {
        sessionFactory.getCurrentSession().delete(persistentObject);
    }

    /**
     * {@inheritDoc}
     */
    @SuppressWarnings(SUPPRESS_WARNINGS_UNCHECKED)
    @Override
    public T find(ID id)
    {
        if (id != null)
        {
            return (T) sessionFactory.getCurrentSession().get(entityClass, id);
        }
        return null;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public T findByParameter(String key, Object value)
    {
        return findByParameter(key, value, false);
    }

    /**
     * {@inheritDoc}
     */
    @SuppressWarnings(SUPPRESS_WARNINGS_UNCHECKED)
    @Override
    public T findByParameter(String key, Object value, boolean retreiveDeleted)
    {
        if (StringUtils.hasText(key) && value != null)
        {
            StringBuilder queryStr = new StringBuilder("from ");
            queryStr.append(entityClassName).append(" as _etc_");
            queryStr.append(" where _etc_.").append(key).append("= :VAL");

            if (!retreiveDeleted)
            {
                queryStr.append(" and _etc_.deleted= :DELETED");
            }

            Query query = sessionFactory.getCurrentSession().createQuery(
                    queryStr.toString());
            query.setParameter("VAL", value);

            if (!retreiveDeleted)
            {
                query.setParameter("DELETED", Boolean.FALSE);
            }

            return (T) query.uniqueResult();
        }
        return null;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean deleteById(ID id)
    {
        if (id != null)
        {
            T entity = find(id);
            if (entity != null)
            {
                sessionFactory.getCurrentSession().delete(entity);
                return true;
            }
        }
        return false;
    }

    /**
     * {@inheritDoc}
     */
    @SuppressWarnings(SUPPRESS_WARNINGS_UNCHECKED)
    @Override
    public List<T> findAll()
    {
        return sessionFactory.getCurrentSession().createCriteria(entityClass)
                .setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY).list();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean exists(ID id)
    {
        if (id != null)
        {
            T entity = find(id);
            if (entity != null)
            {
                return true;
            }
        }
        return false;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean existsByParameter(String key, String value)
    {
        return existsByParameter(key, value, false);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean existsByParameter(String key, String value,
            boolean excludeDeleted)
    {
        if (StringUtils.hasText(key) && StringUtils.hasText(value))
        {
            T entity = findByParameter(key, value, !excludeDeleted);

            if (entity != null)
            {
                return true;
            }
        }
        return false;
    }

    /**
     * @return the sessionFactory
     */
    protected SessionFactory getSessionFactory()
    {
        return sessionFactory;
    }

    /**
     * @return the entityClass
     */
    protected Class<T> getEntityClass()
    {
        return entityClass;
    }

    /**
     * @return the entityClassName
     */
    protected String getEntityClassName()
    {
        return entityClassName;
    }
}
